package edu.utd.text.game.domain.player;

import edu.utd.text.game.domain.areas.DeadEnd;
import edu.utd.text.game.domain.areas.Direction;
import edu.utd.text.game.domain.areas.GameArea;
import edu.utd.text.game.domain.areas.Room;
import edu.utd.text.game.domain.exceptions.DeadEndEncounteredException;
import edu.utd.text.game.domain.exceptions.DuplicateGameObjectException;
import edu.utd.text.game.domain.exceptions.GameObjectNotFoundException;
import edu.utd.text.game.domain.exceptions.InvalidDirectionException;
import edu.utd.text.game.domain.exceptions.ItemAlreadyExistsInInventoryException;
import edu.utd.text.game.domain.exceptions.MaxItemsExceededException;
import edu.utd.text.game.domain.item.GameItem;

public class Player {

	private int score = 0;
	private int maxItemsInInventory = 10;
	private String name = "";
	private Inventory inventory;
	private Room currentRoom;
	
	
	public Player() {
		this.name="Anonymous";
	}

	public Player(String playerName) {
		this.name = playerName;
	}
	  
	  ///////////////////////
	 // Inventory methods //
	///////////////////////
	
	/**
	 * Add an item to the inventory, do not allow duplicates
	 * @param item
	 * @throws MaxItemsExceededException
	 * @throws ItemAlreadyExistsInInventoryException
	 */
	public void addItemToInventory(GameItem item) 
		throws	MaxItemsExceededException,
				ItemAlreadyExistsInInventoryException 
	{		
		Inventory inv = getInventory();
		if(inv.getNumberOfItems() >= maxItemsInInventory) 
			throw new MaxItemsExceededException();
		
		inv.addItem(item);
	}
	
	/**
	 * Remove an item from inventory if found, return boolean success/fail
	 * @param item
	 * @return
	 */
	public boolean removeItemFromInventory(GameItem item) {
		boolean itemRemovedSuccessfully = getInventory().removeItem(item);
		return itemRemovedSuccessfully;
		
	}
	
	/**
	 * Set the inventory instance var to the Inventory object passed in
	 * @param inv
	 * @throws MaxItemsExceededException
	 */
	public void setInventory(Inventory inv) 
		throws	MaxItemsExceededException 
	{
		if(inv.getNumberOfItems() > getMaxItems()) 
			throw new MaxItemsExceededException();
		inventory = inv;
	}
	
	public boolean hasItemOfType(Class itemType) {
		return getInventory().containsItemOfType(itemType);
	}
	
	public boolean hasItem(GameItem anItem) {
		return getInventory().contains(anItem);
	}
	
	public boolean hasItem(String itemName) {
		return getInventory().contains(itemName);
	}
	
	public void pickupItem(GameItem itemToPickup) 
		throws 	GameObjectNotFoundException,
				MaxItemsExceededException,
				ItemAlreadyExistsInInventoryException
	{
		if(currentRoom == null) throw new GameObjectNotFoundException(); //TODO: Create a new exception class for this scenario?
		//may throw MaxItemsExceeded or ItemAlreadyExists		
		addItemToInventory(currentRoom.retrieveGameItem(itemToPickup));
		//if the exception is thrown, we will never get here:
		currentRoom.removeObjectFromRoom(itemToPickup);
	}
	
//	public void pickupItem(Class itemType) 
//		throws 	GameObjectNotFoundException,
//				MaxItemsExceededException,
//				ItemAlreadyExistsInInventoryException
//	{
//		if(currentRoom == null) throw new GameObjectNotFoundException();
//		//may throw MaxItemsExceeded or ItemAlreadyExists
//		pickupItem(currentRoom.retrieveGameItemOfType(itemType));
//	}
	
	public boolean dropItemInRoom(String itemName) {
		try {
			GameItem itemToDrop = getItemFromInventory(itemName);
			getCurrentRoom().addGameObject(itemToDrop);
			return (removeItemFromInventory(itemToDrop));
		} catch (GameObjectNotFoundException gonfx) {
			return false;
		} catch (DuplicateGameObjectException dgox) {
			return false;
		}
	}
	
	public boolean dropItemInRoom(GameItem item) {
		if(this.hasItem(item)) {
			return dropItemInRoom(item.getName());
		}
		return false;
	}
	
	public GameItem getItemFromInventory(String itemName) 
		throws GameObjectNotFoundException
	{
		return getInventory().getItem(itemName);
	}
	
	public boolean canHoldMoreItems() {
		if(getInventory().getNumberOfItems() < getMaxItems()) {
			return true;
		}
		return false;
	}
	
	  /////////////////////
	 // Player movement //
	/////////////////////

	public void moveDirection(Direction direction) 
		throws 	DeadEndEncounteredException,
				InvalidDirectionException
	{
		switch(direction) {
			case NORTH:	
				GameArea northExit = getCurrentRoom().getNorthExit();
				if(northExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)northExit);
				break;
			case SOUTH:
				GameArea southExit = getCurrentRoom().getSouthExit();
				if(southExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)southExit);
				break;
			case EAST:
				GameArea eastExit = getCurrentRoom().getEastExit();
				if(eastExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)eastExit);
				break;
			case WEST:
				GameArea westExit = getCurrentRoom().getWestExit();
				if(westExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)westExit);
				break;
			case NORTHEAST:
				GameArea northeastExit = getCurrentRoom().getNorthEastExit();
				if(northeastExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)northeastExit);
				break;
			case NORTHWEST:
				GameArea northwestExit = getCurrentRoom().getNorthWestExit();
				if(northwestExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)northwestExit);
				break;
			case SOUTHEAST:
				GameArea southeastExit = getCurrentRoom().getSouthEastExit();
				if(southeastExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)southeastExit);
				break;
			case SOUTHWEST:
				GameArea southwestExit = getCurrentRoom().getSouthWestExit();
				if(southwestExit instanceof DeadEnd) throw new DeadEndEncounteredException();
				setCurrentRoom((Room)southwestExit);
				break;
			default: throw new InvalidDirectionException();
		}
	}
	
	  /////////////
	 // Scoring //
	/////////////
	
	
	public void incrementScoreBy(int incr) {
		score += incr;
	}
	
	public void decrementScoreBy(int decr) {
		score -= decr;
	}
	
	  /////////////
	 // Getters //
	/////////////
	
	public Inventory getInventory() {
		if(inventory == null) {
			inventory = new Inventory();
		}
		return inventory;
	}
	
	public Room getCurrentRoom() {
		return currentRoom;
	}
	
	public int getMaxItems() {
		return maxItemsInInventory;
	}
	
	public int getScore() {
		return score;
	}
	
	public String getName() {
		return name;
	}
	
	  /////////////
	 // Setters //
	/////////////
	
	public void setCurrentRoom(Room currentRoom) {
		this.currentRoom = currentRoom;
	}
	
	public void setMaxItems(int max) {
		maxItemsInInventory = max;
	}
	
	public void setScore(int newScore) {
		score = newScore;
	}
	
}
